/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.skatiger.jrove.component;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.el.ValueExpression;

/**
 * A collection of static helper methods for locating UIComponents.
 * 
 * @author Manfred Geiler (latest modification by $Author: skitching $)
 * @version $Revision: 676298 $ $Date: 2008-07-13 05:31:48 -0500 (Sun, 13 Jul 2008) $
 */
public class _ComponentUtils {
    private _ComponentUtils() {
    }

    /**
     * find parent component of class type
     * 
     * @param <T>
     *            Type of class
     * @param component
     *            component will to find
     * @param type
     *            parent type
     * @return find component
     */
    static UIComponent findParentComponent(UIComponent component,
            Class<? extends UIComponent> type) {
        UIComponent parent = component.getParent();
        while (parent != null) {
            if (type.isAssignableFrom(parent.getClass())) {
                return parent;
            }
            parent = parent.getParent();
        }
        return null;
    }

    static UIComponent[] findChidlrenComponent(UIComponent component,
            Class<? extends UIComponent> type) {
        List<UIComponent> children = component.getChildren();
        if (children.isEmpty()) {
            return new UIComponent[0];
        }
        List<UIComponent> _subcoms = new ArrayList<UIComponent>();
        for (UIComponent _component : children) {
            if (type.isAssignableFrom(_component.getClass())) {
                _subcoms.add(_component);
            }
        }
        return _subcoms.toArray(new UIComponent[_subcoms.size()]);
    }

    static UIComponent findParentNamingContainer(UIComponent component,
            boolean returnRootIfNotFound) {
        UIComponent parent = component.getParent();
        if (returnRootIfNotFound && parent == null) {
            return component;
        }
        while (parent != null) {
            if (parent instanceof NamingContainer)
                return parent;
            if (returnRootIfNotFound) {
                UIComponent nextParent = parent.getParent();
                if (nextParent == null) {
                    return parent; //Root
                }
                parent = nextParent;
            } else {
                parent = parent.getParent();
            }
        }
        return null;
    }

    static UIComponent getRootComponent(UIComponent component) {
        UIComponent parent;
        for (;;) {
            parent = component.getParent();
            if (parent == null)
                return component;
            component = parent;
        }
    }

    /*
     * Return true if the specified component matches the provided id.
     * This needs some quirks to handle components whose id value gets
     * dynamically "tweaked", eg a UIData component whose id gets
     * the current row index appended to it.
     */
    private static boolean idsAreEqual(String id, UIComponent cmp) {
        if (id.equals(cmp.getId())) {
            return true;
        }

        return false;
    }

    /**
     * Find the component with the specified id starting from the specified
     * component.
     * <p>
     * Param id must not contain any NamingContainer.SEPARATOR_CHAR characters
     * (ie ":"). This method explicitly does <i>not</i> search into any
     * child naming container components; this is expected to be handled
     * by the caller of this method.
     * <p>
     * For an implementation of findComponent which does descend into
     * child naming components, see org.apache.myfaces.custom.util.ComponentUtils.
     * 
     * @return findBase, a descendant of findBase, or null.
     */
    static UIComponent findComponent(UIComponent findBase, String id) {
        if (idsAreEqual(id, findBase)) {
            return findBase;
        }

        for (Iterator it = findBase.getFacetsAndChildren(); it.hasNext();) {
            UIComponent childOrFacet = (UIComponent) it.next();
            if (!(childOrFacet instanceof NamingContainer)) {
                UIComponent find = findComponent(childOrFacet, id);
                if (find != null)
                    return find;
            } else if (idsAreEqual(id, childOrFacet)) {
                return childOrFacet;
            }
        }

        return null;
    }

    static <T> T getExpressionValue(UIComponent component, String attribute,
            T overrideValue, T defaultValue) {
        if (overrideValue != null) {
            return overrideValue;
        }
        ValueExpression ve = component.getValueExpression(attribute);
        if (ve != null) {
            return (T) ve.getValue(component.getViewContext().getELContext());
        }
        return defaultValue;
    }

    public static String getPathToComponent(UIComponent component) {
        StringBuffer buf = new StringBuffer();

        if (component == null) {
            buf.append("{Component-Path : ");
            buf.append("[null]}");
            return buf.toString();
        }

        getPathToComponent(component, buf);

        buf.insert(0, "{Component-Path : ");
        buf.append("}");

        return buf.toString();
    }

    public static void getPathToComponent(UIComponent component,
            StringBuffer buf) {
        if (component == null)
            return;

        StringBuffer intBuf = new StringBuffer();

        intBuf.append("[Class: ");
        intBuf.append(component.getClass().getName());
        if (component instanceof UIViewRoot) {
            intBuf.append(",ViewId: ");
            intBuf.append(((UIViewRoot) component).getViewId());
        } else {
            intBuf.append(",Id: ");
            intBuf.append(component.getId());
        }
        intBuf.append("]");

        buf.insert(0, intBuf.toString());

        getPathToComponent(component.getParent(), buf);
    }
}
